﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XmlStudio.DataLayer;
using XmlStudio.Enums;
using XmlStudio.Interfaces;
using XmlStudio.Utilities;

namespace XmlStudio.Models.Actions {
    internal sealed class CopySubtreeAction : XmlModelAction {
        private BigXmlNodeInternal subtreeToCopy;
        private BigXmlNodeInternal copiedSubtree;
        private BigXmlNodeInternal subtreeToCopyParent;
        private BigXmlNodeInternal subtreeToCopyLeftSibling;
        private bool subtreeToCopyModified;

        private BigXmlNodeInternal destination;
        private bool destinationModified;
        private InsertMode insertMode;
        private BigXmlNodeInternal referenceNode;

        public CopySubtreeAction(IXmlModelWithNotifications model, BigXmlNodeInternal subtreeToCopy, BigXmlNodeInternal destination, InsertMode insertMode, BigXmlNodeInternal referenceNode)
            : base(model) {
            this.subtreeToCopy = subtreeToCopy;
            this.copiedSubtree = CopySubtree(subtreeToCopy);
            this.destination = destination;
            this.insertMode = insertMode;
            this.referenceNode = referenceNode;

            this.subtreeToCopyParent = this.subtreeToCopy.Parent as BigXmlNodeInternal;
            this.subtreeToCopyLeftSibling = this.subtreeToCopyParent.GetLeftSibling(subtreeToCopy);
            this.subtreeToCopyModified = this.subtreeToCopy.Modified;
            this.destinationModified = this.destination.Modified;
        }

        protected override void ExecuteCore() {
            copiedSubtree.SetParent(destination);

            switch(this.insertMode) {
                case InsertMode.First:
                    destination.ChildNodes.Insert(0, copiedSubtree);
                    destination.XmlNode.InsertBefore(copiedSubtree.XmlNode, destination.XmlNode.FirstChild);
                    break;
                case InsertMode.After:
                    destination.ChildNodes.Insert(destination.ChildNodes.IndexOf(referenceNode), copiedSubtree);
                    destination.XmlNode.InsertAfter(copiedSubtree.XmlNode, referenceNode.XmlNode);
                    break;
                case InsertMode.Before:
                    var index = destination.ChildNodes.IndexOf(referenceNode) - 1;
                    if(index < 0) {
                        index = 0;
                    }

                    destination.ChildNodes.Insert(index, copiedSubtree);
                    destination.XmlNode.InsertBefore(copiedSubtree.XmlNode, referenceNode.XmlNode);
                    break;
                case InsertMode.Last:
                    destination.ChildNodes.Add(copiedSubtree);
                    destination.XmlNode.AppendChild(copiedSubtree.XmlNode);
                    break;
            }

            TreeBuilder.AdjustAddresses(copiedSubtree, this.model);
            this.model.SyncNodes();

            this.copiedSubtree.SetModified(true);
            this.destination.SetModified(true);

            //this.xmlModel.OnModelModified(new Events.ModelModifiedEventArgs(true));
            this.model.SetModified(true);
            this.model.OnSubtreeUpdated(new Events.SubtreeUpdatedEventArgs<BigXmlNode>(destination));
            this.model.OnRawXmlChanged(new Events.RawXmlChangedEventArgs());
        }

        protected override void UnExecuteCore() {
            destination.ChildNodes.Remove(copiedSubtree);
            destination.XmlNode.RemoveChild(copiedSubtree.XmlNode);

            TreeBuilder.AdjustAddresses(destination, this.model);
            this.model.SyncNodes();

            this.destination.SetModified(destinationModified);

            //this.xmlModel.OnModelModified(new Events.ModelModifiedEventArgs(this.modelWasModified));
            this.model.SetModified(this.modelWasModified);
            this.model.OnSubtreeUpdated(new Events.SubtreeUpdatedEventArgs<BigXmlNode>(destination));
            this.model.OnRawXmlChanged(new Events.RawXmlChangedEventArgs());
        }

        public static BigXmlNodeInternal CopySubtree(BigXmlNodeInternal root) {
            var newRoot = root.DeepCopy();
            return newRoot;
        }
    }
}